import logging
from pathlib import Path
from typing import List

from git import Repo, GitCommandError
from unidiff.patch import PatchSet

from utils import Base


class GitMergeHandler(Base):
    def __init__(self, repo_path: str | Path, main_branch_name: str, sub_branch_name: str):
        """
        初始化Git仓库处理器

        Args:
            repo_path: Git仓库的本地路径
        """
        super().__init__()
        self.repo_path = Path(repo_path)
        self.repo = Repo(self.repo_path)
        self.git = self.repo.git

        self.sub_branch_name = sub_branch_name
        self.main_branch_name = main_branch_name

    def get_current_branch(self) -> str:
        """获取当前分支名称"""
        return self.repo.active_branch.name

    def get_conflicts(self) -> List[str]:
        """获取所有冲突文件的列表"""
        return [item for item in self.repo.index.unmerged_blobs()]

    def resolve_conflict(self, file_path: str, resolved_content: str):
        """
        解决指定文件的冲突

        Args:
            file_path: 冲突文件的路径
            resolved_content: 解决冲突后的内容
        """
        # 写入解决后的内容
        with open(self.repo_path / file_path, 'w', encoding='utf-8') as f:
            f.write(resolved_content)

        # 标记文件为已解决
        self.git.add(file_path)
    
    def create_merge_commit_message(self):
        message = f'Merge conflict resolved by {self.name}'
        if message in self.repo.active_branch.commit.message:
            previous_message = self.repo.active_branch.commit.message
            sub_branches, main_branch = previous_message.split('==>')
            sub_branches = filter(lambda x: len(x) > 0, map(str.strip, sub_branches.split('\n')))
            sub_branches = list(map(lambda x: x.strip(), sub_branches))
            sub_branches.append(self.sub_branch_name)
            main_branch = self.main_branch_name
        else:
            branches = self.git.branch('--contains', self.repo.active_branch.commit.hexsha, '--all').split('\n')
            if len(branches) > 1:
                sub_branch = branches[1].strip()
            else:
                sub_branch = ''
                logging.getLogger().error(f'没有sub_branch, {branches}')
            sub_branches = [sub_branch, self.sub_branch_name]
            main_branch = self.main_branch_name
        sub = '\n'.join(sub_branches)

        commit_message = (f'{sub}\n'
                          f'==>\n'
                          f'{main_branch}\n\n'
                          f'{message}')
        return commit_message
    
    def complete_merge(self):
        """
        完成合并操作
        """
        if not self.repo.index.unmerged_blobs():
            commit_message = self.create_merge_commit_message()
            self.git.commit('-m', commit_message)
        else:
            raise Exception("仍有未解决的冲突")

    def abort_merge(self):
        """中止合并操作"""
        self.git.merge('--abort')

    def get_branch_last_commits(self, branch_name: str, count: int = 2) -> List[str]:
        """
        获取指定分支最近的几次提交

        Args:
            branch_name: 分支名称
            count: 需要获取的提交数量

        Returns:
            提交哈希值列表，按时间从新到旧排序
        """
        try:
            commits = list(self.repo.iter_commits(branch_name, max_count=count))
            return [commit.hexsha for commit in commits]
        except Exception as e:
            raise Exception(f"获取分支 {branch_name} 的提交历史失败: {str(e)}")

    def get_diff(self, file_path):
        main_branch = self.main_branch_name
        sub_branch = self.sub_branch_name

        main_branch_commits = self.get_branch_last_commits(main_branch)
        sub_branch_commits = self.get_branch_last_commits(sub_branch)
        if len(main_branch_commits) < 2 or len(sub_branch_commits) < 2:
            raise Exception("分支提交历史不足两次")

        a_commit = main_branch_commits[0]
        base_commit = self.find_common_ancestor()
        b_commit = sub_branch_commits[0]

        a_diff = self.git.diff(base_commit, a_commit, file_path)
        b_diff = self.git.diff(base_commit, b_commit, file_path)
        base_commit_file = self.git.show(f'{base_commit}:{file_path}')
        return a_diff, b_diff, base_commit_file

    def find_common_ancestor(self) -> str:
        """
        找到两个分支的最新共同commit

        Returns:
            如果找到，返回元组 commit_hash
            如果未找到，返回 None
        """
        branch1 = self.main_branch_name
        branch2 = self.sub_branch_name
        try:
            # 使用 merge-base 命令找到共同祖先
            common_commit = self.repo.merge_base(
                self.repo.heads[branch1],
                self.repo.heads[branch2]
            )

            if common_commit:
                # 获取第一个共同祖先（通常只有一个）
                commit = common_commit[0]
                return commit.hexsha
            raise Exception(f"没找到{self.repo_path}中{branch1}和{branch2}的共同commit")

        except Exception as e:
            raise Exception(f"没找到{self.repo_path}中{branch1}和{branch2}的共同commit")

    def handle_merge(self):
        conflicts = self.get_conflicts()
        for file_path in conflicts:
            a_diff, b_diff, base = self.get_diff(file_path)
            for patch in map(lambda d: PatchSet(d), [a_diff, b_diff]):
                target_patch = None
                for patched_file in patch:
                    if patched_file.path == file_path:
                        target_patch = patched_file
                        break
                if target_patch is None:
                    raise FileNotFoundError(f"找不到{file_path}")
                hunks = []
                for hunk in target_patch:
                    block = []
                    for line in hunk:
                        if line.is_added:
                            block.append(line.value)
                        elif line.is_removed:
                            raise ValueError(f"{self.repo_path / file_path}: {self.main_branch_name} <== {self.sub_branch_name}")
                    hunks.append(''.join(block))
                for block in hunks:
                    base += f"\n{block.strip()}\n"
            self.resolve_conflict(file_path, f"{base.strip()}\n\n")
        self.complete_merge()

    def __del__(self):
        self.repo.close()

# 使用示例
def main():
    branch1 = "data_types"
    branch2 = "data_types.TreeNode,TreeParent"
    repo_path = '../tests/temp/proj_e9b678f74caa4dc2'
    try:
        repo = Repo(repo_path)
        repo.git.checkout(branch1)
        repo.git.merge(branch2)
    except GitCommandError as e:
        print(e)
        print(f'{branch1} merge {branch2}')
        # 初始化处理器
        handler = GitMergeHandler(repo_path, branch1, branch2)
        handler.handle_merge()
        pass

if __name__ == "__main__":
    main()